/**
 * Copyright (c) 2008, Andrew Carter
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer. Redistributions in binary form must reproduce 
 * the above copyright notice, this list of conditions and the following disclaimer in 
 * the documentation and/or other materials provided with the distribution. Neither the 
 * name of Andrew Carter nor the names of contributors may be used to endorse or promote 
 * products derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.acarter.jmejtree;

import java.util.ArrayList;
import java.util.Vector;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import com.acarter.jmejtree.util.E_RenderState;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.PassNode;
import com.jme.scene.Spatial;
import com.jme.scene.state.RenderState;

/**
 * @deprecated use <code>JMEComposableTreeModel</code> instead
 * @author Andrew Carter
 */
public class JMEJTreeModel implements TreeModel {

    /**	 */
    private Node rootNode = null;

    private Vector<TreeModelListener> listeners;

    /**
     * Returns the root of the tree. Returns null only if the tree has no nodes.
     * 
     * @param rootNode
     */
    public JMEJTreeModel(Node rootNode) {

	this.rootNode = rootNode;
	listeners = new Vector<TreeModelListener>();
    }

    /**
     * Returns the child of parent at index index in the parent's child array.
     * parent must be a node previously obtained from this data source. This
     * should not return null if index is a valid index for parent (that is
     * index >= 0 && index < getChildCount(parent)).
     */
    public Object getChild(Object parent, int index) {

	if(parent instanceof Spatial) {

	    int ctrlCount = getControllerCount((Spatial) parent);
	    int rsCount = getRenderStateCount((Spatial) parent);

	    if(index >= 0) {

		// Get a controller
		if(index < ctrlCount) {
		    return getController((Spatial) parent, index);
		}
		// Get a render state
		else if((index >= ctrlCount) && (index < (rsCount + ctrlCount))) {
		    return getRenderState((Spatial) parent, index - ctrlCount);
		}
		// Get an actual child node
		else if(parent instanceof Node) {
		    if((index >= (rsCount + ctrlCount)) && (index < getChildCount(parent)))
			return ((Node) parent).getChild(index - (rsCount + ctrlCount));
		}
	    }
	}

	return null;
    }

    /**
     * Returns the number of children of parent. Returns 0 if the node is a leaf
     * or if it has no children. parent must be a node previously obtained from
     * this data source.
     */
    public int getChildCount(Object parent) {

	if(parent instanceof Spatial) {

	    int children = getControllerCount((Spatial) parent);
	    children += getRenderStateCount((Spatial) parent);

	    if(parent instanceof PassNode) {
		children += ((PassNode) parent).nrPasses();
	    }

	    if(parent instanceof Node)
		children += ((Node) parent).getQuantity();

	    return children;
	}

	return 0;
    }

    /**
     * Returns the index of child in parent. If either parent or child is null,
     * returns -1. If either parent or child don't belong to this tree model,
     * returns -1.
     */
    public int getIndexOfChild(Object parent, Object child) {

	if((parent != null) && (child != null)) {

	    if(parent instanceof Spatial) {

		int ctrlCount = getControllerCount((Spatial) parent);
		int rsCount = getRenderStateCount((Spatial) parent);

		if(child instanceof Controller)
		    return getControllerChildIndex((Spatial) parent, (Controller) child);

		else if(child instanceof RenderState)
		    return getRenderStateChildIndex((Spatial) parent, (RenderState) child) + ctrlCount;

		else if(parent instanceof Node) {
		    if(child instanceof Spatial)
			return ((Node) parent).getChildIndex((Spatial) child) + rsCount + ctrlCount;
		}
	    }
	}

	return -1;
    }

    /**
     * 
     * @param child
     * @return
     */
    public Object getParent(Object child) {

	if(child instanceof Spatial)
	    return ((Spatial) child).getParent();

	return null;
    }

    /**
     * 
     * @param object
     * @return
     */
    public TreePath getPathToRoot(Object object) {

	if(object == null)
	    return null;

	TreePath path = new TreePath(rootNode);

	ArrayList<Object> list = new ArrayList<Object>();

	list.add(0, object);

	while ((object = getParent(object)) != null) {

	    if(object.equals(rootNode))
		break;

	    list.add(0, object);
	}

	for(Object child : list)
	    path = path.pathByAddingChild(child);

	return path;
    }

    /**
     * Returns the root of the tree. Returns null only if the tree has no nodes.
     */
    public Object getRoot() {

	return rootNode;
    }

    /**
     * Returns true if node is a leaf. It is possible for this method to return
     * false even if node has no children. A directory in a file system, for
     * example, may contain no files; the node representing the directory is not
     * a leaf, but it also has no children.
     */
    public boolean isLeaf(Object object) {

	return getChildCount(object) > 0 ? false : true;
    }

    /**
     * Adds a listener for the TreeModelEvent posted after the tree changes.
     */
    public void addTreeModelListener(TreeModelListener l) {

	listeners.add(l);
    }

    /**
     * Removes a listener previously added with addTreeModelListener.
     */
    public void removeTreeModelListener(TreeModelListener l) {

	listeners.remove(l);
    }

    /**
     * 
     * @param e
     */
    public void fireTreeNodesChanged(TreeModelEvent e) {

	for(TreeModelListener l : listeners)
	    l.treeNodesChanged(e);
    }

    /**
     * 
     * @param e
     */
    public void fireTreeNodesInserted(TreeModelEvent e) {

	for(TreeModelListener l : listeners)
	    l.treeNodesInserted(e);
    }

    /**
     * 
     * @param e
     */
    public void fireTreeNodesRemoved(TreeModelEvent e) {

	for(TreeModelListener l : listeners)
	    l.treeNodesRemoved(e);
    }

    /**
     * 
     * @param e
     */
    public void fireTreeStructureChanged(TreeModelEvent e) {

	for(TreeModelListener l : listeners)
	    l.treeStructureChanged(e);
    }

    /**
     * Messaged when the user has altered the value for the item identified by
     * path to newValue. If newValue signifies a truly new value the model
     * should post a treeNodesChanged event.
     */
    public void valueForPathChanged(TreePath path, Object newValue) {

    }

    /**
     * Counts the number of controllers currently attached to the given spatial.
     * 
     * @param spatial
     * @return
     */
    private int getControllerCount(Spatial spatial) {

	int count = 0;

	if(spatial != null)
	    count = spatial.getControllerCount();

	return count;
    }

    /**
     * Returns the index of the controller as it is represented in the tree.
     * 
     * @param spatial
     *            The parent object
     * @param controller
     *            The controller
     * @return Index of the controller in the tree
     */
    private int getControllerChildIndex(Spatial spatial, Controller controller) {

	int index = -1;

	if(spatial != null) {

	    for(Controller c : spatial.getControllers()) {

		index++;

		if(c.equals(controller))
		    break;
	    }
	}

	return index;
    }

    /**
     * Returns the controller of the parent given the index from the tree.
     * 
     * @param spatial
     *            Parent object
     * @param index
     *            Index in tree of the controller
     * @return The controller, or null
     */
    private Controller getController(Spatial spatial, int index) {
	return (spatial != null) ? spatial.getController(index) : null;
    }

    /**
     * Counts the number of render states currently defined on the given
     * spatial.
     * 
     * @param spatial
     *            Spatial to look into
     * @return Number of defined render states
     */
    private int getRenderStateCount(Spatial spatial) {

	int count = 0;

	if(spatial != null) {

	    for(E_RenderState rs : E_RenderState.values()) {

		if(spatial.getRenderState(rs.getType()) != null)
		    count++;
	    }
	}

	return count;
    }

    /**
     * Returns the index of the render state as it is represented in the tree.
     * 
     * @param sceneElement
     *            The parent object
     * @param renderState
     *            The renderstate
     * @return Index of the render state in the tree
     */
    private int getRenderStateChildIndex(Spatial sceneElement, RenderState renderState) {

	int index = -1;

	if(sceneElement != null) {

	    for(E_RenderState rs : E_RenderState.values()) {

		if(sceneElement.getRenderState(rs.getType()) != null)
		    index++;

		if(renderState.getType() == rs.getType())
		    break;
	    }
	}

	return index;
    }

    /**
     * Returns the render state of the parent given the index from the tree.
     * 
     * @param sceneElement
     *            Parent object
     * @param index
     *            Index in tree of the render state
     * @return The render state, or null
     */
    private RenderState getRenderState(Spatial sceneElement, int index) {

	int curIndex = -1;

	if(sceneElement != null) {

	    for(E_RenderState rs : E_RenderState.values()) {

		RenderState state = sceneElement.getRenderState(rs.getType());

		if(state != null) {
		    curIndex++;

		    if(curIndex == index)
			return state;
		}
	    }
	}

	return null;
    }
}
